﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Xms.Abp.BackgroundJob;
using Xms.Abp.Data.Message;

namespace Xms.Abp.Messaging.Smsing;

public class TemplateSmsSender : SmsSenderBase, ITransientDependency
{
    protected IEnumerable<ITemplateSmsSenderProvider> Providers { get; }
    protected SmsSettings SmsSettings { get; }
    protected IServiceProvider Services { get; }
    protected ILogger<TemplateSmsSender> Logger { get; }
    protected IJsonSerializer JsonSerializer { get; }
    public TemplateSmsSender(
        IBackgroundJobManager backgroundJobManager,
        IEnumerable<ITemplateSmsSenderProvider> providers,
        SmsSettings smsSettings,
        IServiceProvider services,
        ILogger<TemplateSmsSender> logger,
        IJsonSerializer jsonSerializer,
        ICurrentTenant currentTenant
        )
        : base(backgroundJobManager, currentTenant)
    {
        Providers = providers;
        SmsSettings = smsSettings;
        Services = services;
        Logger = logger;
        JsonSerializer = jsonSerializer;
    }
    public override async Task QueueAsync(
        SmsMessage smsMessage,
        BackgroundJobPriority priority = BackgroundJobPriority.Normal,
        CancellationToken stoppingToken = default)
    {
        ReplacePlaceHoldersAndFormatParameters(smsMessage);
        await base.QueueAsync(smsMessage, priority, stoppingToken);
    }

    protected override async Task SendSmsAsync(SmsMessage smsMessage, CancellationToken stoppingToken = default)
    {
        var result = await DoSendSmsAsync(smsMessage, stoppingToken);
        var store = Services.GetService<ISmsMessageStore>();
        if (store == null)
        {
            Logger.LogWarning("[{SpecialCode}]短信未持久化保存，内容：", "SendSms", JsonSerializer.Serialize(new
            {
                SmsMessage = smsMessage,
                SendResult = result
            }));
        }
        else
        {
            try
            {
                await store.SaveMessageAsync(smsMessage, result, stoppingToken);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "[{SpecialCode}]短信持久化失败：", "SendSms", JsonSerializer.Serialize(new
                {
                    SmsMessage = smsMessage,
                    SendResult = result
                }));
            }
        }
    }

    public virtual void ReplacePlaceHoldersAndFormatParameters(SmsMessage smsMessage)
    {
        var text = smsMessage.Template;
        IDictionary<string, string> parameters = smsMessage.TemplateParameters;
        foreach (var key in parameters.Keys.ToArray())
        {
            text = text.Replace($"{{{{{key}}}}}", parameters[key]);
        }
        if (text.Contains("{{") && text.Contains("}}"))
        {
            throw new ArgumentException("未知参数：" + text.Split(new string[] { "{{", "}}" }, StringSplitOptions.None)[1]);
        }
        var dict = parameters.ToDictionary(r => r.Key, r => r.Value);
        var keys = smsMessage.ParameterNames;
        parameters.Clear();
        foreach (var key in keys)
        {
            if (!dict.ContainsKey(key))
            {
                throw new ArgumentNullException(key);
            }
            parameters[key] = dict[key];
        }
        smsMessage.Template = text;
    }

    public override void ValidateTemplateParameters(IDictionary<string, string> templateParameters, string[] parameterNames)
    {
        var parameters = new Dictionary<string, string>();
        foreach (var name in parameterNames)
        {
            if (!templateParameters.ContainsKey(name))
            {
                throw new ArgumentNullException(name);
            }
            parameters[name] = templateParameters[name];
        }
        templateParameters.Clear();
        foreach (var kv in parameters)
        {
            templateParameters.Add(kv);
        }
    }

    private async Task<SentResult<SmsSentResultItem>> DoSendSmsAsync(SmsMessage smsMessage, CancellationToken stoppingToken = default)
    {
        var providers = Providers.Where(r => r.IsEnabled);
        if (!providers.Any())
        {
            return new SentResult<SmsSentResultItem>
            {
                Items = smsMessage.PhoneNumbers.Select(r => new SmsSentResultItem { PhoneNumber = r, SentStatus = SentStatus.Faild, ErrorCode = "NotFoundProvider", Message = "短信发送未设置可用的发送提供器" })
            };
        }
        var provider = providers.FirstOrDefault(r => r.ProviderName == SmsSettings.DefaultProvider);
        if (provider == null)
        {
            Logger.LogWarning("[{SpecialCode}]短信发送未设置默认发送提供器，或默认发送提供器未启用！", "SendSms");
            provider = providers.FirstOrDefault();
        }
        var sms = smsMessage;
        if (SmsSettings?.IsForwordEnabled ?? false)
        {
            var dict = SmsSettings.Forwords;
            if (dict.Count > 0)
            {
                var phones = sms.PhoneNumbers;
                var newPhones = phones.Select(r => SmsSettings.Forwords[r]);
                if (newPhones.Any(r => !phones.Contains(r)))
                {
                    sms = new SmsMessage
                    {
                        ClientId = smsMessage.ClientId,
                        ParameterNames = smsMessage.ParameterNames,
                        PhoneNumbers = newPhones.ToArray(),
                        Template = smsMessage.Template,
                        TemplateCode = smsMessage.TemplateCode,
                        TemplateParameters = smsMessage.TemplateParameters,
                    };
                }
            }
        }
        if (!SmsSettings.IsErrorAutoSwitchProvider)
        {
            return await provider.SendSmsAsync(sms, stoppingToken);
        }
        return await DoSendSmsAndTraySwitchAsync(sms, provider, providers.Where(r => r != provider), stoppingToken);
    }

    private async Task<SentResult<SmsSentResultItem>> DoSendSmsAndTraySwitchAsync(SmsMessage smsMessage, ITemplateSmsSenderProvider provider, IEnumerable<ITemplateSmsSenderProvider> providers, CancellationToken stoppingToken = default)
    {
        try
        {
            return await provider.SendSmsAsync(smsMessage, stoppingToken);
        }
        catch (Exception ex)
        {
            //发送失败切换运营商
            if (providers.Any())
            {
                var oldName = provider.ProviderName;
                provider = providers.First();
                Logger.LogWarning(ex, $"[{{SpecialCode}}]使用短信发送提供器{oldName}发送短信时报错，正在切换使用提供器{provider.ProviderName}！", "SendSms");
                return await DoSendSmsAndTraySwitchAsync(smsMessage, provider, providers.Where(r => r != provider), stoppingToken);
            }
            else
            {
                throw;
            }
        }
    }
}
